package eu.jakubiak.dto.copy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedList;

import eu.jakubiak.dto.annotations.DtoComponentType;
import eu.jakubiak.dto.manager.IDtoManager;
import eu.jakubiak.javabean.BeanProperty;

public class DtoCollectionCopyHelper implements IDtoCopyHelper {

	@SuppressWarnings("unchecked")
	public <T> void copy(IDtoManager manager, BeanProperty property, T from,
			T into) {
		try {
			// pobranie kolekcji źródłowej
			Collection<?> fromValue = (Collection<?>) property.getGetterMethod().invoke(from);
			if(fromValue==null) {
				// jeżeli nie ma tej kolekcji to i kolekcja docelowa powinna być nulem
				property.getSetterMethod().invoke(into, (Object)null);
			}
			// pobranie kolekcji docelowej
			Collection<?> intoValue = (Collection<?>) property.getGetterMethod().invoke(into);
			if(intoValue==null) {
				// gdy nie ma tej kolekcji to tworzymy nową kolekcję typu LinkedList
				intoValue = new LinkedList<Object>();
				property.getSetterMethod().invoke(into, intoValue);
			}
			// próbujemy odczytać klasę komponentu
			Class<?> componentClass = getComponentClass(property);
			if(componentClass==null) {
				throw new NullPointerException("Unknow component class for copy collection");
			}
			// kopiujemy
			copy(manager, componentClass, (Collection)fromValue, (Collection)intoValue);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}
	
	
	private Class<?> getComponentClass(BeanProperty property) {
		// zakładam, że mamy do czynienia z kopiowaniem listy
		Class<?> componentClass = null;
		if( property.isAnnotationPresent(DtoComponentType.class) ) {
			componentClass = property.getAnnotation(DtoComponentType.class).value();
		} else {
			// spróbujemy odczytać typ tablicy
			Class<?> type = property.getType();
			componentClass = type.getComponentType();
			if(componentClass==null) {
				// to nie była chyba tablica spróbujmy użyć Java Generic
				Type genericReturnType = property.getGetterMethod().getGenericReturnType();
				if(genericReturnType instanceof ParameterizedType) {
					ParameterizedType parameterizedType = (ParameterizedType)genericReturnType;
					if(parameterizedType.getActualTypeArguments().length==1) {
						componentClass = (Class<?>)parameterizedType.getActualTypeArguments()[0];
					}
				}
			}
		}
		return componentClass;
		
	}

	/**
	 * Kopiowanie kolekcji z from na into
	 * @param <E>
	 * @param <T>
	 * @param manager
	 * @param clazz
	 * @param from
	 * @param into
	 */
	public <E, T extends Collection<E>> void copy(IDtoManager manager,
			Class<E> clazz, T from, T into) {
		into.clear();
		try {
			for (E src : from) {
				E dst;
				dst = manager.copy(clazz, src, clazz.newInstance());
				into.add(dst);
			}
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

}
